import datetime
import random
from flask import request, jsonify, current_app, abort, make_response, Response, g, session
from sqlalchemy import or_

from ihome import rs, db
from ihome.libs.captcha.pic_captcha import captcha
from ihome.libs.yuntongxun.sms import CCP
from ihome.models import User, Area, House, Order, HouseImage, Facility
from ihome.modules.api import api_blu
from ihome.utils.common import get_user_info, upload
from ihome.utils.constants import IMAGE_CODE_REDIS_EXPIRES, SMS_CODE_REDIS_EXPIRES, QINIU_DOMIN_PREFIX, \
    HOUSE_LIST_PAGE_CAPACITY
from ihome.utils.response_code import RET, error_map


# 获取图片验证码
@api_blu.route('/imagecode')
def imagecode():
    # get: 获取参数
    cur = request.args.get('cur')
    pre = request.args.get('pre')

    # 校验参数
    if not cur:
        return abort(403)

    # 生成图片验证码
    img_name, img_text, img_bytes = captcha.generate_captcha()

    # 保存验证码到rs
    try:
        rs.set('imagecode:' + cur, img_text, ex=IMAGE_CODE_REDIS_EXPIRES)
    except BaseException as e:
        current_app.logger.error(e)
        return abort(500)
    # 返回img
    response = make_response(img_bytes)  # type:Response
    response.content_type = 'image/jpeg'
    return response


# 获取短信验证码
@api_blu.route('/smscode', methods=['POST'])
def smscode():
    # 获取参数
    mobile = request.json.get('mobile')
    image_code = request.json.get('image_code')
    image_code_id = request.json.get('image_code_id')

    # 校验参数
    if not all([mobile, image_code, image_code_id]):
        return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

    # 获得真实的图片验证码
    try:
        real_image_code = rs.get('imagecode:' + image_code_id)
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

    # 验证图片验证码
    if image_code.upper() != real_image_code:
        return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

    # 生成4位随机短信验证码
    msg_code = "%04d" % random.randint(0, 9999)
    # 发送短信验证码
    res = CCP().send_template_sms(mobile, [msg_code, SMS_CODE_REDIS_EXPIRES], 1)
    if res != 0:
        return jsonify(errno=RET.THIRDERR, errmsg=error_map[RET.THIRDERR])

    # 保存短信验证码
    try:
        rs.set('msg_code_id' + mobile, msg_code, ex=SMS_CODE_REDIS_EXPIRES)
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

    print(msg_code)
    # 成功
    return jsonify(errno=RET.OK, errmsg='发送' + error_map[RET.OK])


# 注册 /USER
@api_blu.route('/user', methods=['POST', 'GET'])
@get_user_info
def user():
    if request.method == 'POST':
        # 获取参数
        mobile = request.json.get('mobile')
        phonecode = request.json.get('phonecode')
        password = request.json.get('password')

        # 校验参数
        if not all([mobile, phonecode, password]):
            return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

        # 获取真实的短信验证码
        try:
            real_phone_code = rs.get('msg_code_id' + mobile)
        except BaseException as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

        # 校验短信验证码
        if phonecode != real_phone_code:
            return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

        # 注册
        user = User()
        user.mobile = mobile
        user.name = mobile
        user.password = password
        db.session.add(user)
        try:
            db.session.commit()
        except BaseException as e:
            current_app.logger.error(e)
            db.session.rollback()
            return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

        # 返回结果
        return jsonify(errno=RET.OK, errmsg=error_map[RET.OK])
        # 检验是否登录
    user = g.user
    if not user:
        return jsonify(errno=RET.SESSIONERR, errmsg=error_map[RET.SESSIONERR])

    # 已登陆
    user_dict = user.to_dict()
    return jsonify(errno=RET.OK, errmsg=error_map[RET.OK], data=user_dict)


# 登录
@api_blu.route('/session', methods=['POST', 'GET', 'DELETE'])
@get_user_info
def session_in():
    if request.method == 'POST':
        # 获取参数
        mobile = request.json.get('mobile')
        password = request.json.get('password')

        # 校验参数
        if not all([mobile, password]):
            return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

        # 验证帐号密码
        # 根据手机号，取出用户模型
        try:
            user = User.query.filter_by(mobile=mobile).first()
        except BaseException as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

        if not user.check_passowrd(password):
            return jsonify(errno=RET.PWDERR, errmsg=error_map[RET.PWDERR])

        session['user_id'] = user.id
        user.update_time = datetime.datetime.now()
        g.user_id = user.id
        return jsonify(errno=RET.OK, errmsg=error_map[RET.OK])

    # GET方法：
    if request.method == 'GET':
        user = g.user
        if not user:
            return jsonify(errno=RET.SESSIONERR, errmsg=error_map[RET.SESSIONERR])

        data = {
            "user_id": user.id,
            "name": user.name
        }
        return jsonify(errno=RET.OK, errmsg=error_map[RET.OK], data=data)

    # 退出登录
    user = g.user
    if not user:
        return jsonify(errno=RET.SESSIONERR, errmsg=error_map[RET.SESSIONERR])
    session.pop('user_id', None)
    return jsonify(errno=RET.OK, errmsg=error_map[RET.OK])


# 用户名修改
@api_blu.route('/user/name', methods=['POST'])
@get_user_info
def user_name():
    # 判断用户登录
    user = g.user
    if not user:
        return jsonify(errno=RET.SESSIONERR, errmsg=error_map[RET.SESSIONERR])

    # 获取参数
    name = request.json.get('name')

    # 校验参数
    if not name:
        return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

    # 修改数据库
    user.name = name
    db.session.add(user)

    return jsonify(errno=RET.OK, errmsg=error_map[RET.OK])


# 用户修改头像
@api_blu.route('/user/avatar', methods=['POST'])
@get_user_info
def user_avatar():
    # 判断用户是否登录
    user = g.user
    if not user:
        return jsonify(errno=RET.SESSIONERR, errmsg=error_map[RET.SESSIONERR])

    # 获取参数
    avatar_file = request.files.get("avatar")
    print(avatar_file)

    # 参数校验
    try:
        avatar_bytes = avatar_file.read()
        # print(avatar_bytes)
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

    # 修改数据库
    try:
        file_name = upload(avatar_bytes)
        print(file_name)
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.THIRDERR, errmsg=error_map[RET.THIRDERR])

    user.avatar_url = file_name
    db.session.add(user)
    try:
        db.session.commit()
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

    user_dict = user.to_dict()
    # 返回参数
    data = {
        "avatar_url": user_dict['avatar_url']
    }

    return jsonify(errno=RET.OK, errmsg=error_map[RET.OK], data=data)


# 获取用户实名信息/修改用户实名信息
@api_blu.route('/user/auth', methods=['GET', 'POST'])
@get_user_info
def user_auth():
    user = g.user
    # 判断用户是否登录
    if not user:
        return jsonify(errno=RET.SESSIONERR, errmsg=error_map[RET.SESSIONERR])

    # get
    if request.method == 'GET':
        real_name = user.real_name
        id_card = user.id_card
        data = {
            'real_name': real_name,
            'id_card': id_card
        }
        return jsonify(errno=RET.OK, errmsg=error_map[RET.OK], data=data)

    # 修改用户实名信息
    # 获取参数
    real_name = request.json.get('real_name')
    id_card = request.json.get('id_card')

    # 校验参数
    if not all([real_name, id_card]):
        return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

    user.id_card = id_card
    user.real_name = real_name

    db.session.add(user)
    return jsonify(errno=RET.OK, errmsg=error_map[RET.OK])


# 我的房屋发布列表
@api_blu.route('/user/houses')
@get_user_info
def user_houses():
    # 判断用户是否登录
    user = g.user
    if not user:
        return jsonify(errno=RET.SESSIONERR, errmsg=error_map[RET.SESSIONERR])

    # 获取用户房屋列表
    try:
        houses = user.houses
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

    house_list = []
    for house in houses:
        house_dict = house.to_basic_dict()
        house_list.append(house_dict)

    return jsonify(errno=RET.OK, errmsg=error_map[RET.OK], data=house_list)


# 城区列表
@api_blu.route('/areas')
def areas():
    # 获取地区查询对象
    try:
        areas = Area.query.all()
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

    area_list = []
    for area in areas:
        area_dict = area.to_dict()
        area_list.append(area_dict)

    return jsonify(errno=RET.OK, errmsg=error_map[RET.OK], data=area_list)


# 发布房源/ houses房屋数据搜索
@api_blu.route('/houses', methods=['POST', 'GET'])
@get_user_info
def houses():

    # POST发布房源
    if request.method == 'POST':
        # 检测用户登录
        user = g.user
        if not user:
            return jsonify(errno=RET.SESSIONERR, errmsg=error_map[RET.SESSIONERR])
        # 获取参数
        title = request.json.get('title')
        price = request.json.get('price')
        area_id = request.json.get('area_id')
        address = request.json.get('address')
        room_count = request.json.get('room_count')
        acreage = request.json.get('acreage')
        unit = request.json.get('unit')
        capacity = request.json.get('capacity')
        beds = request.json.get('beds')
        deposit = request.json.get('deposit')
        min_days = request.json.get('min_days')
        max_days = request.json.get('max_days')
        facility = request.json.get('facility')

        # 参数校验
        if not all(
                [title, price, area_id, address, room_count, acreage, unit, capacity, beds, deposit, min_days, max_days,
                 facility]):
            return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

        # int转换
        try:
            area_id = int(area_id)
            room_count = int(room_count)
            acreage = int(acreage)
            min_days = int(min_days)
            max_days = int(max_days)
            capacity = int(capacity)
            price = int(price) * 100
            deposit = int(deposit) * 100
        except BaseException as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

        # 建立房间模型
        house = House()

        # 修改数据库
        house.title = title
        house.price = price
        house.address = address
        house.room_count = room_count
        house.acreage = acreage
        house.unit = unit
        house.capacity = capacity
        house.beds = beds
        house.deposit = deposit
        house.min_days = min_days
        house.max_days = max_days
        house.user_id = user.id
        house.area_id = area_id

        # 设施查询
        try:
            facilities = Facility.query.filter(Facility.id.in_(facility)).all()
        except BaseException as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

        house.facilities.extend(facilities)

        # 地区查询
        area = Area.query.filter(Area.id == area_id).first()
        area.houses.append(house)
        db.session.add(house)
        try:
            db.session.commit()
        except BaseException as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

        data = {
            "house_id": house.id
        }

        return jsonify(errno=RET.OK, errmsg=error_map[RET.OK], data=data)

    # GET房屋数据搜索
    # 获取参数
    aid = request.args.get('aid', '')
    sd = request.args.get('sd', '')
    ed = request.args.get('ed', '')
    sk = request.args.get('sk', 'new')
    p = request.args.get('p', 1)

    # 校验参数
    # page校验
    try:
        p = int(p)
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

    # 日期转换
    try:
        sd_date = None
        ed_date = None
        if sd:
            sd_date = datetime.datetime.strptime(sd, '%Y-%m-%d')
        if ed:
            ed_date = datetime.datetime.strptime(ed, 'Y-%m-%d')
        if sd_date and ed_date:
            assert sd_date < ed_date, Exception('开始时间大于结束时间')
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

    # 查询列表
    query_list = []
    # 城区列表查询条件
    if aid:
        try:
            aid = int(aid)
        except BaseException as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])
        else:
            query_list.append(House.area_id == aid)

    # 过滤已经预订的房屋
    conflict_order = None
    try:
        if sd_date and ed_date:
            conflict_order = Order.query.filter(Order.begin_date <= ed_date, Order.end_date >= sd_date).all()
        elif sd_date:
            conflict_order = Order.query.filter(Order.end_date >= sd_date).all()
        elif ed_date:
            conflict_order = Order.query.filter(Order.begin_date <= ed_date).all()
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

    # 过滤订单时间冲突的查询条件
    if conflict_order:
        conflict_house_id = [order.house_id for order in conflict_order]
        query_list.append(House.id.notin_(conflict_house_id))

    # 根据sk排序
    if sk == 'booking':
        house_query = House.query.filter(*query_list).order_by(House.order_count.desc())
    elif sk == 'price-inc':
        house_query = House.query.filter(*query_list).order_by(House.price.asc())
    elif sk == 'price-des':
        house_query = House.query.filter(*query_list).order_by(House.price.desc())
    else:
        house_query = House.query.filter(*query_list).order_by(House.create_time.desc())

    # 分页
    pn = house_query.paginate(p, HOUSE_LIST_PAGE_CAPACITY, False)
    houses_list = [house.to_basic_dict() for house in pn.items]
    total_page = pn.pages
    data = {
        'houses': houses_list,
        'total_page': total_page
    }

    # 返回参数
    return jsonify(errno=RET.OK, errmsg=error_map[RET.OK], data=data)


# 上传房源图片
@api_blu.route('/houses/<int:house_id>/images', methods=['POST'])
def houses_images(house_id):
    # 获取参数
    house_image_file = request.files.get('house_image')
    print(house_id)
    # 校验参数
    try:
        house_image_bytes = house_image_file.read()
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

    try:
        file_name = upload(house_image_bytes)
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.THIRDERR, errmsg=error_map[RET.THIRDERR])

    # 获得房屋模型
    try:
        house = House.query.get(house_id)
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

    house_image_url = file_name
    house.index_image_url = file_name
    # 新建图片模型
    house_image = HouseImage(url=house_image_url)

    house.images.append(house_image)
    db.session.add_all([house, house_image])

    try:
        db.session.commit()
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

    house_dict = house.to_basic_dict()
    data = {
        'url': house_dict['img_url']
    }

    return jsonify(errno=RET.OK, errmsg=error_map[RET.OK], data=data)


# 房屋详情页面
@api_blu.route('/houses/<int:house_id>')
def houses_detail(house_id):
    # 获取房屋模型
    try:
        house = House.query.get(house_id)
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

    house_dict = house.to_full_dict()
    # 房屋转字典
    data = {
        'house': house_dict,
    }
    #
    return jsonify(errno=RET.OK, errmsg=error_map[RET.OK], data=data)


# 首页房屋推荐展示
@api_blu.route('/houses/index')
def houses_index():
    # 查询房屋5个展示
    try:
        houses = House.query.order_by(House.id).limit(5).all()
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

    house_list = []
    for house in houses:
        house_dict = house.to_basic_dict()
        house_list.append(house_dict)

    return jsonify(errno=RET.OK, errmsg=error_map[RET.OK], data=house_list)


# 添加订单/订单列表
@api_blu.route('/orders', methods=['POST', 'GET', 'PUT'])
@get_user_info
def orders():
    # 判断用户登录
    user = g.user
    if not user:
        return jsonify(errno=RET.SESSIONERR, errmsg=error_map[RET.SESSIONERR])

    # post添加订单
    if request.method == 'POST':
        # 获取参数
        house_id = request.json.get('house_id')
        start_date = request.json.get('start_date')
        end_date = request.json.get('end_date')

        # 校验参数
        if not all([house_id, start_date, end_date]):
            return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

        # 校验int
        try:
            house_id = int(house_id)
        except BaseException as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

        # 日期转换
        try:
            start_date = datetime.datetime.strptime(start_date, '%Y-%m-%d')
            end_date = datetime.datetime.strptime(end_date, '%Y-%m-%d')
        except BaseException as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

        # 获取house模型
        try:
            house = House.query.get(house_id)
        except BaseException as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

        if house.user_id == user.id:
            return jsonify(errno=RET.ROLEERR, errmsg=error_map[RET.ROLEERR])
        # 数据库操作
        order = Order()
        order.user_id = user.id
        order.house_id = house.id
        order.begin_date = start_date
        order.end_date = end_date
        order.days = (end_date - start_date).days
        order.house_price = house.price
        order.amount = order.days * order.house_price
        order.order_count += 1
        # 加入事物
        db.session.add(order)
        # 数据库提交
        try:
            db.session.commit()
        except BaseException as e:
            current_app.logger.error(e)
            db.session.rollback()
            return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

        data = {
            'order_id': order.id
        }
        return jsonify(errno=RET.OK, errmsg=error_map[RET.OK], data=data)

    if request.method == 'GET':
        # get订单列表
        # 获取参数
        role = request.args.get('role')

        # 校验参数
        if role not in ['custom', 'landlord']:
            return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

        # 房主的房子
        try:
            houses = user.houses
        except BaseException as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

        # filter 列表
        filter_list = []
        # 房主名下的房子的id
        for house in houses:
            filter_list.append(Order.house_id == house.id)

        # 房子名下的订单
        if role == 'custom':
            try:
                orders = Order.query.filter(or_(*filter_list)).all()
                order_list = [order.to_dict() for order in orders]
            except BaseException as e:
                current_app.logger.error(e)
                return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])
        # 房主自己下的订单
        else:
            try:
                order_list = [order.to_dict() for order in user.orders]
            except BaseException as e:
                current_app.logger.error(e)
                return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])
        data = {
            'orders': order_list
        }
        return jsonify(errno=RET.OK, errmsg=error_map[RET.OK], data=data)

    # PUT请求方式 接单和拒单
    # 获取参数
    action = request.json.get('action')
    order_id = request.json.get('order_id')
    reason = request.json.get('reason')

    # 校验参数
    if not all([action, order_id]):
        return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

    # id转换
    try:
        order_id = int(order_id)
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

    # action 判断
    if action not in ['accept', 'reject']:
        return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

    # 获取订单模型
    try:
        order = Order.query.get(order_id)
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

    if action == 'accept':
        order.status = 'WAIT_COMMENT'
    else:
        order.status = 'REJECTED'
        order.comment = reason

    db.session.add(order)
    return jsonify(errno=RET.OK, errmsg=error_map[RET.OK])


# 评论
@api_blu.route('/orders/comment', methods=['PUT'])
@get_user_info
def orders_comment():
    # 判断用户登录
    user = g.user
    if not user:
        return jsonify(errno=RET.SESSIONERR, errmsg=error_map[RET.SESSIONERR])

    # 获取参数
    comment = request.json.get('comment')
    order_id = request.json.get('order_id')

    # 校验参数
    if not all([comment, order_id]):
        return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

    # id int
    try:
        order_id = int(order_id)
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])

    # 数据库查询订单模型
    try:
        order = Order.query.get(order_id)
    except BaseException as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])

    # 修改订单评论
    order.comment = comment
    db.session.add(order)
    return jsonify(errno=RET.OK, errmsg=error_map[RET.OK])
